March 22, 2005

Git

今日の趣旨

  1. gitを覚える (頭で)
  2. 呪文を覚える (身体で)
  3. 呪文を打ち消す呪文を覚える (身体で)

2と3をひたすら繰り返す.

重犯罪人に自分の罪を思い知らせるなら、土を掘り移動させ、それからまた埋めて元に戻す、という作業を延々と繰り返させればよい。それは究極の拷問であり、最後には精神に異常をきたすであろう。(ドフトエフスキー?)

Gitってなんだよ

プログラムのソースコードなどの変更履歴を記録・追跡するための分散型バージョン管理システムだよ.

なにができんだよ

ディレクトリを好きな時点の状態に復元できるよ.

Google driveでよくね?

よくない.

Google driveでできなくいことの例

  • RemoteとSyncするタイミングを制御できない(変更した瞬間自動的にSync)
    • 共有directoryの中身を自分のPCでだけ前のバージョンに戻す
    • 複数人で同じdirectoryの中身を編集する
  • directoryのバックアップを取るタイミングを制御できない
    • コードが動く状態を保存しておきたいとかができない
  • directoryのバックアップに含めるfileを制御できない
    • 設定ファイルはPCごとに別にしたいのでSyncしたくないとか思ってもできない

  • あとこうならないですむ

頭で覚えること

Gitには3つの視点がある

  • Commit tree
  • Three areas
  • File status

これらを理解すればGit master.

Commit tree

Commit (directoryのスナップショット)からなるツリー.

  • CommitはdirectoryのスナップショットにIDとしてhash値を振ったもの.
  • Commitには親となるCommitが記録されている (= ツリー構造)
  • master, HEAD, branchなどはCommitを指しているTag (Pointer)の名前
  • 1度作ったcommitは基本的になくならない(探せなくはなるので注意)

どういうときに思い出すべき?

  • branchの操作 (branch, checkout, reset, rebase, push, pull)
  • commitの操作 (reset, cherry-pick)

Three areas

1つのCommitの裏には3つのレイヤがある

  1. Working directory: 実際のdirectory
  2. Staging Area (Index): Working direcoryのファイルのうちCommitに含まれる予定のファイルのcopy
  3. git directory: Staging areaのfileのcopy

どういうときに思い出すべき?

  • 特定のCommitの変更 (reset, checkout)
  • 特定のfileの操作 (add, rm, mv, reset, stash)

File status

fileには4つの状態がある

  1. Untracked: gitの追跡対象外のfile
  2. Unmodified: gitの追跡対象で変更されていないfile(=Commitのfileと同じ)
  3. Modified: gitの追跡対象で変更されたfile(=Commitのfileと違う)
  4. Staged: gitの追跡対象で変更されたfileでStaging areaにcopy済みのもの

どういうときに思い出すべき?

  • 特定のfileの操作 (add, rm, mv, reset, stash)

おめでとう

あなたは,gitマスターになりました.

add, commit, reset

git status

異世界に行ったら最初に唱える呪文.

File statusが4つのどの状態にあるかの確認.

# 準備
cd path/to/workspace
mkdir repo000
cd repo000
git status 

これから何かする度になるべくgit statusしましょう.

思い出す

git init

カレントディレクトリをgit repositoryにする

git init 

git initをなかったことにする

カレントディレクトリからgit direcoryを削除する

rm -R .git

git add

ファイルをStaged areaに追加する.

  • 新規ファイルの場合
    • tracked(gitの追跡対象指定)にする
    • staged(commitに含める対象指定)にする.
  • 既存ファイルの場合
    • staged(commitに含める対象指定)にする.

git add [path ...]

echo "ROW000" > file000
git add file000

git addをなかったことにする

  • 新規ファイルの場合
    • untracked(gitの追跡対象外)にする
    • unstaged(commitに含める対象外)にする.
  • 既存ファイルの場合
    • unstaged(commitに含める対象外)にする.

git reset [--soft | --hard] [<commit>] [path ...]

git reset HEAD file000

stage(git add)されたすべてのファイルを対象にしたい場合

git reset HEAD

git commit

staged(git add)されたすべてのファイルのコピーをGit Directoryへ記録する

# 準備
git add file000

git commit [--amend] [-m <msg>]

git commit -m "Added file000."

git commitをなかったことにする

Git Directoryから最後のCommitを削除する

# 準備
cp file000 file001
git add file001
git commit -m "Added file001."
git reset --soft HEAD^ # HEAD^ = 1つ前の親Commit

addもなかったことに

# 復習
git reset HEAD 

commitとaddの2つをまとめてなかったことに

git reset HEAD^ 

git commitをなかったことにしたのをなかったことに

$ git reflog
e170b24 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
953913d HEAD@{1}: commit: Added file001. <-- resetするまえのHEADの位置
...
git reset HEAD@{1} # HEAD@{1} = 1つ前にHEADの指していたcommit

git reset

HEAD インデックス 作業ディレクトリ 作業ディレクトリ保護の有無

Commit Level

reset –soft [commit]

REF

いいえ

いいえ

はい

reset [commit]

REF

はい

いいえ

はい

reset –hard [commit]

REF

はい

はい

いいえ

checkout [commit]

HEAD

はい

はい

はい

File Level

reset (commit) [file]

いいえ

はい

いいえ

はい

checkout (commit) [file]

いいえ

はい

はい

いいえ

つまり?

git reset --hard するときは気をつけよう.

rm, mv

git rm –cached

ファイルをStaged areaとGit directoryから削除する

(=untracked(gitの追跡対象外)にする)

git rm [--cached] [path ...]

cd path/to/workspace
mkdir repo002
echo "ROW000" > file000
git add file000 
git commit -m "Added file000."
# 復習
git add file000 # tracked(gitの追跡対象指定)にする
git rm --cached file000

.gitignore

一生untrackedにしておきたいfileやdirectoryは,.gitignoreというfileを作ってそこに書く.

.gitignoreしたdirectory内の一部のfileのみをtrackedにしたい場合は,.gitkeepに書いてignoreされないようにする.

.gitignore(.gitkeep)については,自分でググってね. (i.e., あまり興味ない)

git rm

ファイルをWorking Directory, Staged area, Git directoryのすべてから削除する.

# 準備  
git add file000
rm file000
git add file000 # git rm --cached file000でも良い
git commit -m "Removed file000."

上の2つをまとめてやる

# 準備  
git reset --hard HEAD^ # HEAD^ = 1つ前の親Commit
git rm file000
git commit -m "Removed file000."

git mv

ファイルをWorking Directory, Staged area, Git directoryのすべてにおいてmoveまたはrenameする.

Note: mvとrenameは本質的に同じ

git mv <source> <destination>

# 準備  
git reset --hard HEAD^
git mv file000 file001
git commit -m "Renamed file000 to file001."

commit –amend, rebase

git commit –amend

最後のCommitを変更する

# 準備
cd path/to/workspace
mkdir repo003
echo "ROW000" > file000
cp file000 file001
git add file000
git commit -m "Added file000."

ファイルをaddし忘れたときとか,コメントを修正したい時とかに便利

git add file001 
git commit --amend -m "Added file000 and file001."

git rebase

直前だけじゃなく,もっと前まで遡って自分の都合の良いように歴史を改変したい

git rebase -i <commit>

<commit>からHEADまでのコミットをまとめて変更する

# 準備
for f in file001 file002 file003 gomi; do cp file000 $f; done 
# file001, file002, file003, gomiができれば何でも良い
git add file001
git commit -m "Added file001."
git add file002
git commit -m "Added file002."
git add file003
git commit -m "「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される"
git add gomi 
git commit -m "Added gomi."

git rebase -i <commit>

* bf1b31a (HEAD -> master) Added gomi. # 
* fb7cc7b 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される # <-- HEAD~1 or HEAD^
* 469a642 Added file002. # <-- HEAD~2 or HEAD^^
* f8c8773 Added file001. # <-- HEAD~3 or HEAD^^^
* 2312317 Added file000. # <-- HEAD~4 or HEAD^^^^

<commit>には変更の根本となるcommitを指定する.

git rebase -i HEAD~4 # HEAD~4 = 4つ前の親Commit

この場合変更対象はHEAD - HEAD~3

pick 17b9da7 Added file001.
pick 98ee317 Added file002.
pick 9a7d9db 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
pick 00805cf Added gomi.

# Rebase e170b24..00805cf onto e170b24 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

1番目と2番目はまとめたくて,3番目のメッセージを書き直したくて,4番目は消したい.

pick 17b9da7 Added file001.
s 98ee317 Added file002.  
e 9a7d9db 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
d 00805cf Added gomi. 

# Rebase e170b24..00805cf onto e170b24 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

# This is a combination of 2 commits.
# This is the 1st commit message:

Added file001.

# This is the commit message #2:

Added file002.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sat Feb 16 22:50:41 2019 +0900
#
# interactive rebase in progress; onto e170b24
# Last commands done (2 commands done):
#    pick 5b6d77f Added file001.
#    squash 21c25a0 Added file002.
# Next commands to do (2 remaining commands):
#    edit 2f734eb 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
#    drop cf942f2 Added gomi.
# You are currently rebasing branch 'master' on 'e170b24'.
#
# Changes to be committed:
#       new file:   file001
#       new file:   file002

# This is a combination of 2 commits.
# This is the 1st commit message:

Added file001 and file002.

# This is the commit message #2:

# Added file002.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sat Feb 16 22:50:41 2019 +0900
#
# interactive rebase in progress; onto e170b24
# Last commands done (2 commands done):
#    pick 5b6d77f Added file001.
#    squash 21c25a0 Added file002.
# Next commands to do (2 remaining commands):
#    edit 2f734eb 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
#    drop cf942f2 Added gomi.
# You are currently rebasing branch 'master' on 'e170b24'.
#
# Changes to be committed:
#       new file:   file001
#       new file:   file002

[detached HEAD 0136ee2] Added file001 and file002.
 Date: Sat Feb 16 22:50:41 2019 +0900
 2 files changed, 2 insertions(+)
 create mode 100644 file001
 create mode 100644 file002
Stopped at 2f734eb...  「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
You can amend the commit now, with

  git commit --amend # <-- commitを編集したいのでまずは,こっち

Once you are satisfied with your changes, run

  git rebase --continue

git logを見てみると<commit>で指定した根本から枝分かれしていることがわかる

$ git log --oneline --graph --all
* 87449ac (HEAD) 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
* 2f98c92 Added file001 and file002.
| * bf1b31a (master) Added gomi.
| * fb7cc7b 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
| * 469a642 Added file002.
| * f8c8773 Added file001.
|/
* 2312317 Added file000.

git commit --amend
「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sat Feb 16 22:50:41 2019 +0900
#
# interactive rebase in progress; onto e170b24
# Last commands done (3 commands done):
#    squash 21c25a0 Added file002.
#    edit 2f734eb 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
# Next command to do (1 remaining command):
#    drop cf942f2 Added gomi.
# You are currently editing a commit while rebasing branch 'master' on 'e170b24'.
#
# Changes to be committed:
#       new file:   file003
#

Added file003.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sat Feb 16 22:50:41 2019 +0900
#
# interactive rebase in progress; onto e170b24
# Last commands done (3 commands done):
#    squash 21c25a0 Added file002.
#    edit 2f734eb 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
# Next command to do (1 remaining command):
#    drop cf942f2 Added gomi.
# You are currently editing a commit while rebasing branch 'master' on 'e170b24'.
#
# Changes to be committed:
#       new file:   file003
#

終わったらcontinue

git rebase --continue

git rebaseをなかったことにする

rebaseの途中でやっぱりやめたくなった

git rebase --abort

git rebaseをなかったことにする

rebaseしたあと,やっぱりなかったことにしたくなった

rebaseは既存のcommitをコピーを作っているだけなので,元のcommitも残っている(見えなくなっているだけ).

* 99d7024 (master) Added file003
* 2f98c92 Added file001 and file002.
| * bf1b31a (HEAD) Added gomi. # <-- 元のHEAD
| * fb7cc7b 再帰で検索して「もしかして:再帰」をクリックすると…。
| * 469a642 Added file002.
| * f8c8773 Added file001.
|/
* 2312317 Added file000.

git rebaseをなかったことにする

見えないcommitをたどるにはreflog.

$ git reflog
a264b19 (HEAD -> master) HEAD@{0}: rebase -i (finish): returning to refs/heads/master
a264b19 (HEAD -> master) HEAD@{1}: commit (amend): Added file003.
ecc3571 HEAD@{2}: rebase -i (edit): 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
9070db1 HEAD@{3}: rebase -i (squash): Added file001 and file002.
408797f HEAD@{4}: rebase -i (start): checkout HEAD~4
92b7c29 HEAD@{5}: commit: Added gomi.  # <-- rebase前のHEADの位置
...
git reset --hard HEAD@{5} # HEAD@{5} = 5つ前にHEADの指していたcommit

git rebaseをなかったことにしたのをなかったことにする

$ git reflog
92b7c29 (HEAD -> master) HEAD@{0}: reset: moving to HEAD@{5} # <-- さっきのreset
a264b19 (HEAD -> master) HEAD@{1}: rebase -i (finish): returning to refs/heads/master
a264b19 (HEAD -> master) HEAD@{2}: commit (amend): Added file003.
ecc3571 HEAD@{3}: rebase -i (edit): 「もしかして:再帰」をクリックすると「もしかして:再帰」が表示される
9070db1 HEAD@{4}: rebase -i (squash): Added file001 and file002.
408797f HEAD@{5}: rebase -i (start): checkout HEAD~4
92b7c29 HEAD@{6}: commit: Added gomi. 
...
git reset --hard HEAD@{1} 

git rebaseまとめ

  • 指定したcommitを分岐点として改変された歴史作る
  • 途中でわけわかんなくなったら–abort

branch

git log

基本の呪文 Commit treeを表示する

git log --oneline --graph --all

defaultのgit logは見にくいので,いい感じで表示してくれるwrapperもある.

$ git-foresta --all | less -RSX
99d7024c 2019-02-19 10:31  ○ ameono (master) Added file003
2f98c925 2019-02-19 10:31  ● ameono Added file001 and file002.
bf1b31ad 2019-02-19 10:31  │ ○ ameono (HEAD) Added gomi.
fb7cc7b0 2019-02-19 10:31  │ ● ameono 再帰で検索して「もしかして:再帰」をクリックすると…。
469a6426 2019-02-19 10:31  │ ● ameono Added file002.
f8c8773e 2019-02-19 10:31  │ ● ameono Added file001.
                           ├─┘
2312317f 2019-02-19 10:30  ■ ameono Added file000.

思い出す

Commit (directoryのスナップショット)からなるツリー.

  • CommitはdirectoryのスナップショットにIDとしてhash値を振ったもの.
  • Commitには親となるCommitが記録されている (= ツリー構造)
  • master, HEAD, branchなどはCommitを指しているTag (Pointer)の名前
  • 1度作ったcommitは基本的になくならない(探せなくはなるので注意)

git branch

# 準備
cd path/to/workspace
git init
echo "ROW000" > file000
git add file000 
git commit -m "Added file000."

git branch [-d|-D] <branch-name> [<start-point>]

git branch feature/update_file000 master

feature/branch000という名前のタグを作る(指す先はmasterの指しているところ)

git branchをなかったことにする(branchを削除する)

git branch -d feature/update_file000

強制的に削除する場合

git branch -D feature/update_file000

branchの削除をなかったことにする

$ git branch -d feature/update_file000
Deleted branch feature/update_file000 (was e170b24). # <-- ここのhashを覚えておく 
git checkout e170b24 
git checkout -b feature/update_file000

git checkout

git checkout [-b] [<branch>|<commit>]

git checkout feature/update_file000

HEADの指す先をfeature/branch000へ変える

branchの作成とcheckoutをいっぺんにやる

git checkout -b feature/update_file000

git branch testing 

git checkout testing 

CLIで見てみる

$ git branch feature/update_file000
$ git log --graph --all
* commit e170b24890ca2685d9414b15f3862a28b16b3828 (HEAD -> master, feature/branch000)
  Author: ameono <4m3on0@gmail.com>
  Date:   Sat Feb 16 21:28:33 2019 +0900

      Added file000.
$ git checkout feature/update_file000
$ git log --graph --all
* commit e170b24890ca2685d9414b15f3862a28b16b3828 (HEAD -> feature/branch000, master)
  Author: ameono <4m3on0@gmail.com>
  Date:   Sat Feb 16 21:28:33 2019 +0900

      Added file000.

HEAD -> の指す先に注目.

echo "ROW001" >> file000
git add file000 
git commit -m "Updated file000 (added the second row)." 

masterと違うCommitを指していることを確認

git log --oneline --graph --all
git checkout master
cat file000 # update_file000ブランチの変更が反映されていないことを確認 
cp file000 file001 
git add file001
git commit -m "Added file001."

分岐したことを確認

git log --oneline --graph --all

git merge

他のbranchの先端と今のbranchの先端を統合するcommitを作る

git merge [<branch>]

git merge

GithubでPull requestをmergeするときはこんな感じのことをしている.

git checkout feature/update_file000
git merge master 
$ git log --oneline --graph --all
*   0b45d32 (HEAD -> feature/update_file000) Merge branch 'master' into feature/update_file000
|\
| * 179a3bc (master) Added file001.
* | cf7cb13 Updated file000 (added the second row).
|/
* e170b24 Added file000.

Merge commitができている.

git checkout master 
git merge --no-ff feature/update_file000
$ git log --oneline --graph --all
*   500adbb (HEAD -> master) Merge branch 'feature/update_file000'
|\
| *   0b45d32 (feature/update_file000) Merge branch 'master' into feature/update_file000
| |\
| |/
|/|
* | 179a3bc Added file001.
| * cf7cb13 Updated file000 (added the second row).
|/
* e170b24 Added file000.

Merge commitがまたできている.

git mergeをなかったことにする

$ git log --oneline --graph --all
*   500adbb (HEAD -> master) Merge branch 'feature/update_file000'
|\
| *   0b45d32 (feature/update_file000) Merge branch 'master' into feature/update_file000
| |\
| |/
|/|
* | 179a3bc Added file001. # <-- Merge前のmaterの位置
| * cf7cb13 Updated file000 (added the second row). # <-- Merge前のfeature/update_file000の位置
|/
* e170b24 Added file000.

master tagとupdate_file000 tagをそれぞれMerge前の位置に戻せば良い.

(Interruption) HEADについて

(復習) HEAD^ = 1つ前の親Commit

親が2つ以上ある場合 HEAD^N (N=親の番号) のように指定できる.

$ git show --name-only HEAD^
commit 179a3bce92c28cb9a6ac1ef4d8ff4002c83d2f7d
Author: ameono <4m3on0@gmail.com>
Date:   Sun Feb 17 00:57:33 2019 +0900

    Added file001.

$ git show --name-only HEAD^2
commit 0b45d32e5332152a5a44400d6394344199f4f968 (feature/update_file000)
Merge: cf7cb13 179a3bc
Author: ameono <4m3on0@gmail.com>
Date:   Sun Feb 17 14:51:11 2019 +0900

    Merge branch 'master' into feature/update_file000

HEAD~2と似てるけど,全然意味が違う.

$ git show --name-only HEAD~2
commit e170b24890ca2685d9414b15f3862a28b16b3828
Author: ameono <4m3on0@gmail.com>
Date:   Sat Feb 16 21:28:33 2019 +0900

    Added file000.

  • HEAD~N = HEADのN個前の親Commit

  • HEAD^N = HEADの1個前の親CommitのN番目

ex) 1個前の親の2番めの親Commit = HEAD~1^2

  • HEAD@{N} = N個前にHEADが指していたCommit

git mergeをなかったことにする (続き)

masterのMergeをなかったことに

git reset --hard HEAD^ 
$ git log --oneline --graph --all
*   0b45d32 (feature/update_file000) Merge branch 'master' into feature/update_file000
|\
| * 179a3bc (HEAD -> master) Added file001.
* | cf7cb13 Updated file000 (added the second row).
|/
* e170b24 Added file000.

update_file000のMergeをなかったことに

git checkout feature/update_file000
git reset --hard HEAD^ 
$ git log --oneline --graph --all
* 179a3bc (master) Added file001.
| * cf7cb13 (HEAD -> feature/update_file000) Updated file000 (added the second row).
|/
* e170b24 Added file000.

簡単ですね?

今までの話が理解できていれば,mergeをなかったことにしたのをなかったことにもできるはず.

git merge –no-ff について

(復習)
git checkout feature/update_file000
git merge master 
git checkout master
git merge --no-ff feature/update_file000 <-- --no-ffに注目

no-ff = No Fast Forward

なしにしてやってみる

git checkout feature/update_file000
git merge master 
git checkout master
git merge feature/update_file000
$ git log --oneline --graph --all
*   e6de5fd (HEAD -> master, feature/update_file000) Merge branch 'master' into feature/update_file000
|\
| * 179a3bc Added file001.
* | cf7cb13 Updated file000 (added the second row).
|/
* e170b24 Added file000.

git rebase

git rebase [<branch>]

git rebase --continue | --skip | --abort | --quit |

他のbranchの先端に今のbranchの根本から先端までのcommitをまとめて移植する

# 準備
git branch -f feature/update_file000 HEAD^2
git reset --hard HEAD^
git checkout feature/update_file000
git rebase master
$ git log --oneline --graph --all
* a336ea3 (HEAD -> feature/update_file000) Added file001.
* cf7cb13 (master) Updated file000 (added the second row).
* e170b24 Added file000.

$ git rebase feature/update_file000
First, rewinding head to replay your work on top of it...
Fast-forwarded master to feature/update_file000.

$ git log --oneline --graph --all
* a336ea3 (HEAD -> master, feature/update_file000) Added file001.
* cf7cb13 Updated file000 (added the second row).
* e170b24 Added file000.

git rebaseをなかったことにする

まずはmasterを戻す.

$ git reset --hard HEAD^ 
* a336ea3 (feature/update_file000) Added file001.
* cf7cb13 (HEAD -> master) Updated file000 (added the second row).
* e170b24 Added file000.

次にupdate_file000を戻す

$ git reflog
a336ea3 (HEAD -> feature/update_file000) HEAD@{2}: checkout: moving from master to feature/update_file000
cf7cb13 (master) HEAD@{3}: reset: moving to HEAD^
a336ea3 (HEAD -> feature/update_file000) HEAD@{4}: rebase finished: returning to refs/heads/master
a336ea3 (HEAD -> feature/update_file000) HEAD@{5}: rebase: checkout feature/update_file000
cf7cb13 (master) HEAD@{6}: checkout: moving from feature/update_file000 to master
a336ea3 (HEAD -> feature/update_file000) HEAD@{7}: rebase finished: returning to refs/heads/feature/update_file000
a336ea3 (HEAD -> feature/update_file000) HEAD@{8}: rebase: Added file001.
cf7cb13 (master) HEAD@{9}: rebase: checkout master
179a3bc HEAD@{10}: checkout: moving from master to feature/update_file000 # <-- これ
$ git reset --hard HEAD@{10} 
$ git log --oneline --graph --all
* 179a3bc (HEAD -> feature/update_file000) Added file001.
| * cf7cb13 (master) Updated file000 (added the second row).
|/
* e170b24 Added file000.

Mergeより戻すのが面倒(reflog使わないかん)ですね.

このことから,rebaseとmergeならmergeを使うべきという人もいます.

公式曰く,

公開リポジトリにプッシュしたコミットをリベースしてはいけない

この指針に従っている限り、すべてはうまく進みます。もしこれを守らなければ、あなたは嫌われ者となり、友人や家族からも軽蔑されることになるでしょう。(Pro Git)

Merge VS rebase

git cherry-pick

git cherry-pick <commit>...

他のbranchの特定のcommitを今のbranchの先端に移植する

$ git log --oneline --graph --all
* 179a3bc (HEAD -> feature/update_file000) Added file001.
| * cf7cb13 (master) Updated file000 (added the second row).
|/
* e170b24 Added file000.
$ git cherry-pick cf7cb13
$ git log --oneline --graph --all
* 8a1eae4 (HEAD -> feature/update_file000) Updated file000 (added the second row).
* 179a3bc Added file001.
| * cf7cb13 (master) Updated file000 (added the second row).
|/
* e170b24 Added file000.

masterのcommitがfeature/update_file000へcopyされた

rebaseはcherry-pickを複数まとめてやっているだけとも言える.

Conflict

# 準備
git reset --hard HEAD^
echo "ROW002 <- update_file000" >> file000
$ git merge master
Auto-merging file000
CONFLICT (content): Merge conflict in file000
Automatic merge failed; fix conflicts and then commit the result.
ROW000
<<<<<<< HEAD
ROW002 <- update_file000
=======
ROW001
>>>>>>> master

Conflictの解消法

  1. ConfilitしたファイルをEditerで編集する
  2. git checkout [--theirs|--ours] <paths> を使う
  3. merge-toolを使う

2. checkoutを使う方法

git checkout --theirs file000
ROW000
<<<<<<< HEAD # = --ours 
ROW002 <- update_file000 
=======
ROW001
>>>>>>> master # = --theris 

merge元のbranch(=–theirs)の変更かmerge先(=HEAD, –ours)の変更かのどちらかを採用する.

欠点: どういう結果になるか予想がつきにくい.

3. merge-toolを使う

merge-toolには色々ある.

git mergetool --tool=vimdiff

DiffがあるChunk(=色が変わっている部分)にカーソルを合わせ, 1do, 2do, 3doと入力することで,それぞれ上段の左から1番目,2番目,3番目が今後の姿に取り込まれる.

PyCharmのようにConflictを解消するツールが付いているIDEもある.

Github

Githubってなんだよ

GitHub は世界最大の Remote Git リポジトリホスティングサービスだよ.

つまり?

localのrepository = localのPCにおいてあるdirecotry

とすると,

Github上のrepository = Google driveにおいてあるdirecotry

のような関係.

有名どころ

  • Github

  • Gitlab

  • Bitbucket

Githubがやたら有名だが他にもある (Google driveとDropbox的な関係).

できることは大差ないので,お財布事情とUIの好みの問題

(あとは狸と猫のどっちが好きかとか…バケツ…).

Github

  • とにかく有名 (連携サービスが多いイメージ).

  • お金たくさん持ってく.

  • Github Educationとか言う学生プランに加入するとGithub含め色々な有名サービスが無料で使える.

  • 本田圭佑がいる

  • 他の2つと違い自前のCIとかはない(他のツールと組み合わせる前提OSSの思想的には○?)

Gitlab

  • どちらかというと,自組織のサーバにインストールして自分たちしかアクセスできないRemote repositoryとして使う (要するに自前のfile server的なやつ)オンプレミス版のほうが有名.

  • Slackを水で薄めたようなchatアプリやCIがついてくるので,これひとつで開発に必要なすべての作業を完結させることができる.

  • 大人の事情でPublic臭の強いGithubとかSlackとかを使えない人たちが使ってるっぽい(NASAとか).

Bitbucket

  • 一昔前までPrivate repository(外部公開されない) 作り放題な唯一のサービスだったので,個人の利用者が多くいた(今は,Githubも作り放題).

  • Source treeの開発元でもあるAtlassianの製品.

  • お金ないらしい(最近値上げした).

比較

https://stackshare.io/stackups/bitbucket-vs-github-vs-gitlab

2019 2月現在,各サービスの無料プランの状況

Github Bitbucket Github (学生) Bitbucket (学生) GitLab
private repository 無制限 無制限 無制限 無制限 無制限
public repository 無制限 無制限 無制限 無制限 無制限
collaborator 3名まで 5名まで 無制限 無制限 無制限
CI build time - 50分/月 - 500分/月 2000分/月
LFS 1GB 1GB 1GB 5GB 無制限?
LFS Bandwidth 1GB/月 無制限 1GB/月 無制限 無制限?

git clone

remote repositoryのcopyをlocalに作る.(= ダウンロード)

git clone <repository>

git fetch

remote repository(のタグとcommit)を同期する

git pull

git pull [--rebase] <remote> <branch>

git fetchgit merge origin/master をまとめてやる(--rebaseつけるとgit rebase origin/masterになる)

git push

git push <remote> <branch>

remoteの<branch>タグをlocalの<branch>タグの位置に同期させる(Conflictする場合はpushできない).

FYI: remote側で起こる動作は,<origin>/<branch>checkoutして

git merge --ff-only <branch>

したときに起こる動作と同じと考えることができる.

--ff-only勉強しといてよかったね!!!?

Github Flow

gitのgit

最初のコミット

ブロックチェーンとGit

Gitの名前の由来